package mpp;

import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicMarkableReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

class Ex4q1 {

	static PQueue<Object> queue;
	static int counter = 0;
	
	public static void main(String[] args) {
		int n;
		
		if (args.length != 2) {
			System.out.println("Wrong number of arguments (Not 2).");
			return;
		}
		if (args[0].toUpperCase().equals("LAZY"))
		{
			queue = new LazyQueue<Object>();
		}
		else if (args[0].toUpperCase().equals("LOCKFREE"))
		{
			queue = new LockFreeListQueue<Object>();
		}
		try {
			n = Integer.parseInt(args[1]);
		} catch (Exception e) {
			System.out.println("Wrong Input");
			return;
		}
		

		Thread[] threads = new Thread[n];

		long start = System.currentTimeMillis();
		
		// Initialize threads
		for (int i = 0; i < n; i++) {
			threads[i] = new Thread() {

				final int ITERATIONS = 100000;

				public void run() {
					Random random = new Random();
					for (int i = 0; i < ITERATIONS; i++) {
						queue.add(new Object(), random.nextInt());
						queue.removeMin();
					}
				}
			};
		}

		// start the threads
		for (int i = 0; i < n; i++) {
			threads[i].start();
		}

		// waiting for the threads to end
		try {
			for (int i = 0; i < n; i++) {
				threads[i].join();
			}
		} catch (InterruptedException e) {
		}

		System.out.format("Finished after %d miliseconds.\n",
				new Long((System.currentTimeMillis() - start)));
	}
}



class NodeEx4<T> {
	T value;
	int key = -1;
	AtomicMarkableReference<NodeEx4<T>> next;

	NodeEx4(T newValue, int key) {
		value = newValue;
		this.key = key;
		next = new AtomicMarkableReference<NodeEx4<T>>(null, false);
	}
}

class Window<T> {
	public NodeEx4<T> pred;
	public NodeEx4<T> curr;

	Window(NodeEx4<T> pred, NodeEx4<T> curr) {
		this.pred = pred;
		this.curr = curr;
	}
}

interface PQueue<T> {
	void add(T item, int score);

	T removeMin();
}





/* Ex4Q2 */
class Ex4q2 {

	private static Lock _lock;
	static int counter = 0;
	static int n = 0;
	
	public static void main(String[] args) {
		
				
		if ( args.length != 2 && !(args[0].toUpperCase().equals("CLH")
				|| args[0].toUpperCase().equals("TTAS1")
				|| args[0].toUpperCase().equals("TTAS2")))
		{
			System.out.println("Wrong Input");
			return;
		}
		
		try{
			n = Integer.parseInt(args[1]);
		} catch (Exception ex){
			ex.printStackTrace();
			return;
		}
			
		if (args[0].toUpperCase().equals("CLH"))
			_lock = new CLHLock();
		else if (args[0].toUpperCase().equals("TTAS1"))
			_lock = new BackoffLock();
		else _lock = new MyBackoffLock();
			
			
		Thread[] threads = new Thread[n];

		long start = System.currentTimeMillis();
		
		// Initialize threads
		for (int i = 0; i < n; i++) {
			threads[i] = new Thread() {

				int ITERATIONS = 1000000;

				public void run() {
					for (int i = 0; i < ITERATIONS; i++)
					{
						_lock.lock();
						try
						{
							int temp = counter;
							temp++;
							counter = temp;
						} finally
						{
							_lock.unlock();
						}
					}
				}			
			};
		}

		// start the threads
		for (int i = 0; i < n; i++) {
			threads[i].start();
		}

		// waiting for the threads to end
		try {
			for (int i = 0; i < n; i++) {
				threads[i].join();
			}
		} catch (InterruptedException e) {
		}

		System.out.format("Finished after %d miliseconds.\n",
				new Long((System.currentTimeMillis() - start)));
	}
}

class MyBackoffLock implements Lock{

	 private AtomicBoolean state = new AtomicBoolean(false);  
	  private static final int MIN_DELAY = 4;
	  private static final int MAX_DELAY = 2048;
	  
	  
	  
	  public void lock() {
	    Backoff backoff = new Backoff(MIN_DELAY, MAX_DELAY);
	    while (true) {
	      while (state.get()) {}	// spin
	      if (!state.getAndSet(true)) { // try to acquire lock
	        return;
	      } else {			// backoff on failure
	        try {
	          backoff.backoff();
	        } catch (InterruptedException ex) {
	        }
	      }
	    }
	  }  
	  public void unlock() {
	    state.set(false);
	  }
	  
	  // Any class implementing Lock must provide these methods
	  public Condition newCondition() {
	    throw new java.lang.UnsupportedOperationException();
	  }
	  
	  public boolean tryLock(long time,
	      TimeUnit unit)
	      throws InterruptedException {
	    throw new java.lang.UnsupportedOperationException();
	  }
	  
	  public boolean tryLock() {
	    throw new java.lang.UnsupportedOperationException();
	  }
	  
	  public void lockInterruptibly() throws InterruptedException {
	    throw new java.lang.UnsupportedOperationException();
	  }
	
}
